<?php
/*
* Copyright (c) 2010 by Cediti
*
* You may distribute under the terms of either the GNU Lesser General
* Public License or the new BSD License, as specified in the README file.
*/
// created :	31 March 2010
// version :	31 March 2010

abstract class Cmis_Http_Adapter
{
    const METHOD_GET = 'GET';
    const METHOD_POST = 'POST';
    const METHOD_DELETE = 'DELETE';
    const METHOD_PUT = 'PUT';

    const TIMEOUT = 10;

	protected $_auth_login;
	protected $_auth_password;
	protected $_auth_type;

	protected $_config;
	protected $_secure;

	/**
	 * The type (cmis, atom, ...) that we wait
	 */
	protected $_return_type;
	protected $_return_check;

	protected $_errors;

	/**
	 * @var Cmis_Http_Response
	 */
	protected $_response;

    /**
     * Build a HTTP adapter, default = Curl
     * @return Cmis_Http_Adapter
     */
    public static function factory($type = null, $secure = false)
    {
        $http_client = 'Cmis_Http_Adapter_' . ucfirst($type);

        if (!class_exists($http_client, true))
        {
            throw new Cmis_Http_Exception('No adapter can be found', Cmis_Http_Exception::NO_ADAPTER_FOUND);
        }

        return new $http_client($secure);
    }

	public function __construct($secure = false)
	{
        $this->_auth_type = Cmis_Request_Interface::AUTH_NONE;
        $this->_secure = $secure;
		$this->reset();
	}

	public function __destruct()
	{
		$this->close();
	}

    /**
     * By default, all previous options will be reloaded ecept if we made a reset
     * @return Cmis_Http_Adapter
     */
    public function reset()
    {
        $this->close();
        $this->_config = array();
        $this->_infos = array();
        $this->_response = null;

        $this->_reset();

        return $this;
    }

	/**
	 * Return the info corresponding to the key or the whole info if the key is null
	 * @param string $key
     * @return array|string
	 */
	public function getInfo($key = null)
	{
        return ((is_null($key)) ? $this->_infos : $this->_infos[ $key ]);
	}

	/**
	 * @return Cmis_Http_Response
	 */
	public function getResponse()
	{
		return $this->_response;
	}

	/**
	 * @return array
	 */
	public function getError()
	{
		return $this->_errors;
	}
    
    /**
     * Return all the headers if $key is null else the $key option
     * @param string $key
     * @return array|string
     */
    public function getHeader($key = null)
    {
        return ((is_null($key)) ? $this->_config['header'] : $this->_config['header'][ $key ]);
    }

    /**
     * Define a header option
     * @param string $key
     * @param mixed $value
     * @return Cmis_Http_Adapter
     */
	public function setHeader($key, $value)
	{
        $this->_config['header'][ $key ] = $value;
        return $this;
	}

	/**
	 * Check the return type
	 * @param bool $check
	 * @return Cmis_Http_Adapter
	 */
	public function setCheckReturnType($check)
	{
		$this->_return_check = $check;
        return $this;
	}

	/**
	 * Define a configuration option
	 * @param string $key
	 * @param mixed $value
	 * @return Cmis_Http_Adapter
	 */
	public function setOption($key, $value)
	{
		if (!empty($value))
		{
			$this->_config['options'][ $key ] = $value;
		}

		return $this;
	}

    public function setContentType($content)
    {
        $this->setHeader('Content-Type', $content);
        return $this;
    }

	/**
	 * Return all the options if $key is null else the $key option
	 * @param string $key
	 * @return array|string
	 */
	public function getOptions($key = null)
	{
		return ((is_null($key)) ? $this->_config['options'] : $this->_config['options'][ $key ]);
	}

	public function doGet($url)
	{
		$this->_doRequest(self::METHOD_GET, $url);
	}

	public function doPost($url, $data, $mimeType, array $extras = null)
	{
		$this->_doRequest(self::METHOD_POST, $url, $data, $mimeType, $extras);
	}

    public function doDelete($url)
    {
        $this->_doRequest(self::METHOD_DELETE, $url);
    }

    public function doPut($url)
    {
        die('-[Must be implemented : ' . __METHOD__ . ' (' . __FILE__ . '#' . __LINE__ . ')]-');
    }

	/**
	 * Defined the current auth
	 * 
	 * After this a call to reset is made. So call this before making a request!
	 * 
	 * @param string $login
	 * @param string $password
	 * @param const $type
	 * @return Cmis_Http_Adapter
	 */
	public function setAuth($login, $password, $type = Cmis_Request_Interface::AUTH_HTTP_BASIC)
	{
		$this->_auth_type = $type;
		$this->_auth_login = $login;
		$this->_auth_password = $password;
        $this->reset();

        return $this;
	}

	/**
	 * Add an error
	 * @param int $code
	 * @param string $message
	 * @return void
	 */
    protected function _setError($code, $message)
    {
        $this->_errors[] = array($code, $message);
    }

    /**
     * Do a query
     * @param const $method query method
     * @param string $url urL to call
     * @param array|string $data
     * @param string $mimeType
     * @param array $extras
     * @return void
     */
    protected function _doRequest($method, $url, $data = null, $mimeType = '', array $extras = null)
    {
        if (empty($url))
        {
            throw new Cmis_Exception_Request_Rest('Url is empty');
        }

        if (!empty($mimeType))
        {
        	$this->setContentType($mimeType);
        }

        $this->_preRequest($method, $data, $mimeType, $extras);

        switch($this->_auth_type)
        {
            case Cmis_Request_Interface::AUTH_NONE :
                break;
            default :
            	if (!$this->_auth($method, $url, $data, $mimeType, $extras))
            	{
            		throw new Cmis_Http_Exception('Auth system can\'t be use', Cmis_Http_Exception::BAD_AUTH);
            	}
                break;
            case Cmis_Request_Interface::AUTH_URL :
                $url = $url . ((strpos('?', $url) ? '&' : '?') . $this->_auth_login . '=' . $this->_auth_password);
                break;
        }
        
        try
        {
        	$response = $this->_request($method, $url, $data, $mimeType, $extras);
        	$this->_response = new Cmis_Http_Response($response, $this->getInfo());
        }
        catch(Cmis_Exception_Http $e)
        {
        	$errors = $this->getError();
        	//put last error message in exception
            throw new Cmis_Exception_Request_Rest($errors[ count($errors) - 1 ][1], Cmis_Exception_Request_Rest::FOLLOW_UP, $e);
        }

        if (Debug::is_active())
        {
            Debug::var_debug($method, $url, $data, $mimeType);
        }

        //check for header if needed
        if ($this->_return_check)
        {
            if ($this->getResponse()->getHTTPCode() == 200 && !$this->getResponse()->checkResponseHeader($this->_returnType))
	        {
	            throw new Cmis_Exception_Request_Request('Bad return type (' . $this->getResponse()->getHeader('Content-Type') . ')');
	        }
        }

        if ($this->getResponse()->getHTTPCode() > 300)
        {
        	if(Debug::is_active())
        	{
        		Debug::var_debug($this);
        	}
        	throw new Cmis_Exception_Request_BadHTTPCode($this->getResponse()->getHTTPStatus(), $this->getResponse()->getHTTPCode());
        }

        $this->_postRequest($method, $url, $data, $mimeType, $extras);
    }

    /**
     * @return bool false if not intercepted
     */
    protected function _auth($method, $url, $data, $mimeType, $extras)
    {
    	return false;
    }

    /**
     * Method to be ovewritten by subclass
     * Update $this->_return_type if needed
     * 
     * Before making a request we can prepare it, initialized some object, ...
     * 
     * @return void
     */
    protected function _preRequest($method, $data, $mimeType, $extras)
    {}

    /**
     * Method to be ovewritten by subclass
     * 
     * After a request, checking mime type and allowed the response we sometimes
     * need to do some operation like closing the socket
     * 
     * @return void
     */
    protected function _postRequest($method, $url, $data, $mimeType, $extras)
    {}

    /**
     * Call inside _doRequest after preRequest (and auth) and before postRequest
     * Update $this->_return_type if needed
     * 
     * Just after the authentification we do a request and return the answer
     * 
     * @return string the response
     */
    abstract protected function _request($method, $url, $data, $mimeType, $extras);

    /**
     * Reset call at the end of reset()
     * @return void
     */
    abstract protected function _reset();

    /**
     * Close the current connection if needed
     * @return Cmis_Http_Adapter
     */
    abstract public function close();
}

# EOF
